/*
  Copyright (c) 2011, Mikheev Rostislav
  Copyright (c) 2011, FreshFarsh team

  Steel Engine source code is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
/**
  @file window.cpp
  @author Mikheev Rostislav <hacenator@gmail.com>
  @date 27.03.2011
  @brief GUI window class source file
*/

#include "gui/window.h"

namespace _STEEL_NAMESPACE {

GUIWindow::GUIWindow(const char* name, int x, int y, int width, int height, GUISkin* skin)
  : _x(x),
  _y(y),
  _width(width),
  _height(height),
  _control_focused(NULL),
  _control_mouse_over(NULL),
  _control_mouse_down(NULL),
  _index_buffer(0),
  _vertex_buffer(0),
  _texture_coordinate_buffer(0)
{
	//_name = strAlloc(name);

	_visible = true;
	//FILE *fcfg = fopen(cfgname, "rb");
	//fread(texture_array, sizeof(float), 32,  fcfg);
	//fread(corner_offset, sizeof(int), 2,  fcfg);
	//fclose(fcfg);
	DoResize();
};

GUIWindow::~UIWindow() {
  DeleteBuffers();

  delete [] _name;
};

void GUIWindow::DeleteBuffers(){
  #define DELETEBUFFER(buffer) if(glIsBuffer(buffer)){ glDeleteBuffers(1, &buffer); }
	DELETEBUFFER(_index_buffer);
	DELETEBUFFER(_vertex_buffer);
	DELETEBUFFER(_texture_coordinate_buffer);
	#undef GLDELETEBUFFER
}

void GUIWindow::Draw(){
  if(!_visible)
    return;

	glDisable(GL_CULL_FACE);

	glActiveTexture(GL_TEXTURE0);
	glClientActiveTexture(GL_TEXTURE0);
	glEnable(GL_TEXTURE_2D);
	//glBindTexture(GL_TEXTURE_2D, _textureId); //TODO(hacenator): Get texture from current GUI skin

	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

  // vertex buffer
	glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer);
	glVertexPointer(3, GL_FLOAT, 0, NULL);

  // texture coord buffer
	glBindBuffer(GL_ARRAY_BUFFER, _texture_coordinate_buffer);
	glTexCoordPointer(2, GL_FLOAT, 0, NULL);

  // indexes buffer
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _index_buffer);

	// Draw
	glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_SHORT, NULL);

  glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	DrawControl();
}

void GUIWindow::Motion(int action, float x, float y) {
	#define IN_RECT(rect) (x > rect.left && x < rect.right && y > rect.top && y < rect.bottom)
	//sListElement * el;

	_console->Infof("GUIWindow(%s)::motion (%d, %f, %f)", _name, action, x, y);

	// check "mouse out" event for children controls
	if (_control_mouse_over != NULL) {
		if (!IN_RECT(_control_mouse_over->getRect())) {
			_control_mouse_over->MoveOut(this);
			_control_mouse_over = NULL;
		}
	}

	// mouse move
	if ((action == mMove) && (_mouseDownControl != NULL))
		_mouseDownControl->MouseMove(this, (int)x, (int)y);

	el = _list.first;
	while (el != NULL) {
		if ((el->control->visible()) && (IN_RECT(el->control->getRect()))) {
			//LOG_INFO("UIWindow(%s)::motion '%s' in rect", _name, el->control->getName());
////////////////////////////
////   UP
			if (action == mUp) {
				//LOG_INFO("UIWindow(%s)::motion mUp (%f, %f)", _name, x, y);
				//LOG_INFO("UIWindow focused on '%s'", el->control->getName());

				// предыдущий контрол теряет фокус
				if ((_focusedControl != NULL) && (_focusedControl != el->control)) {
					//LOG_INFO("UIWindow send FocusLost to '%s'", _focusedControl->getName());
					_focusedControl->FocusLost(this);
				}

				// новый контрол получает фокус
				if (_focusedControl != el->control) {
					//LOG_INFO("UIWindow send FocusGet to '%s'", el->control->getName());
					el->control->FocusGet(this);
				}
				_focusedControl = el->control;

				// Click только если MouseUp и MouseDown на одном контроле
				if (_mouseDownControl == el->control) {
					//LOG_INFO("UIWindow call '%s' click", el->control->getName());
					el->control->Click(this);
				} else { // иначе просто MouseUp для теущего
					el->control->MouseUp(this, (int)x, (int)y);
				}

				if (_mouseDownControl != NULL) // если MouseUp клик был до его обработки
					_mouseDownControl->MouseUp(this, (int)x, (int)y);
				_mouseDownControl = NULL;
////////////////////////////
////   DOWN
			} else if (action == mDown) {
				//LOG_INFO("UIWindow(%s)::motion mDown (%f, %f)", _name, x, y);

				_mouseDownControl = el->control;
				_mouseDownControl->MouseDown(this, (int)x, (int)y);
////////////////////////////
////   MOVE
			} else if (action == mMove) {
				//LOG_INFO("UIWindow(%s)::motion mMove (%f, %f)", _name, x, y);

				// в фокусе будет _mouseDownControl пока не MouseUp
				if (_mouseDownControl == NULL) {
					// ушел на другой контрол
					if ((_moveOverControl != NULL) && (_moveOverControl != el->control)) {
						_moveOverControl->MoveOut(this);
					}
					//
					_moveOverControl = el->control;
					el->control->MoveOver(this, (int)x, (int)y);
				}
			}

			break; // не проверять остальные контролы
		}
		el = el->next;
	}
	#undef IN_RECT
}

void UIWindow::Keyboard(int state, int key) {
	if (_focusedControl != NULL) {
		LOG_INFO("UIWindow::Keyboard to '%s' #%d[%d]", _focusedControl->getName(), key, state);
		if (state == kDown) {
			_focusedControl->KeyDown(this, key);
		} else {
			_focusedControl->KeyUp(this, key);
		}
	}
}

void UIWindow::DrawControl() {
	sListElement * wnd;

	wnd = _list.first;
	while (wnd != NULL) {
		wnd->control->Draw();
		wnd = wnd->next;
	}
}

void UIWindow::DoResize(){
/*	0 -- 1 -- 2 -- 3
	|	 |	  |	   |
	|	 |	  |    |
	4 -- 5 -- 6 -- 7
	|	 |	  |    |
	|	 |	  |	   |
	8--- 9 -- 10 - 11
	|	 |	  |	   |
	|	 |	  |	   |
	12 - 13 - 14 - 15
*/
	GLDELETEBUFFER(m_indexBuffer);
	GLDELETEBUFFER(m_vertexBuffer);
	GLDELETEBUFFER(m_texCoordBuffer);
	// заполняем буфер вершин
#define VERTEXARRAY(index, x,y) vertex_array[index][0] = (float)x; vertex_array[index][1] = (float)y; vertex_array[index][2] = 0;
	VERTEXARRAY(0, _x, _y); VERTEXARRAY(1, _x+corner_offset[0], _y); VERTEXARRAY(2, _x+_width-corner_offset[0], _y); VERTEXARRAY(3, _x+_width, _y);
	VERTEXARRAY(4, _x, _y+corner_offset[1]); VERTEXARRAY(5, _x+corner_offset[0], _y+corner_offset[1]); VERTEXARRAY(6, _x+_width-corner_offset[0], _y+corner_offset[1]); VERTEXARRAY(7, _x+_width, _y+corner_offset[1]);
	VERTEXARRAY(8, _x, _y+_height-corner_offset[1]); VERTEXARRAY(9, _x+corner_offset[0], _y+_height-corner_offset[1]); VERTEXARRAY(10, _x+_width-corner_offset[0], _y+_height-corner_offset[1]); VERTEXARRAY(11, _x+_width, _y+_height-corner_offset[1]);
	VERTEXARRAY(12, _x, _y+_height); VERTEXARRAY(13, _x+corner_offset[0], _y+_height); VERTEXARRAY(14, _x+_width-corner_offset[0], _y+_height); VERTEXARRAY(15, _x+_width, _y+_height);
	/*vertex_array[0][0] = 0; vertex_array[0][1] = 0; vertex_array[0][2] = 0;
	vertex_array[1][0] = 0; vertex_array[1][1] = 32; vertex_array[1][2] = 0;
	vertex_array[2][0] = 16; vertex_array[2][1] = 0; vertex_array[2][2] = 0; */

#undef VERTEXARRAY
	// буфер индексов
#define INDEXARRAY(quad_num, a,b,c,d) index_array[quad_num*6] = a; index_array[quad_num*6+1] = b; index_array[quad_num*6+2] = c;\
	index_array[quad_num*6+3] = c; index_array[quad_num*6+4] = d; index_array[quad_num*6+5] = a;

	INDEXARRAY(0, 0,4,5,1); INDEXARRAY(1, 1,5,6,2); INDEXARRAY(2, 2,6,7,3);
	INDEXARRAY(3, 4,8,9,5); INDEXARRAY(4, 5,9,10,6); INDEXARRAY(5, 6,10,11,7);
	INDEXARRAY(6, 8,12,13,9); INDEXARRAY(7, 9,13,14,10); INDEXARRAY(8, 10,14,15,11);

#undef INDEXARRAY
	//index_array[0] = 0; index_array[1] = 4; index_array[2] = 2;

 // буфер текстурных координат
//#define TEXTUREARRAY(index, u, v) texture_array[index][0] = u; texture_array[index][1] = 1-v;
//	TEXTUREARRAY(0, 0,0); TEXTUREARRAY(1, 0.5f,0); TEXTUREARRAY(2, 0.5f,0); TEXTUREARRAY(3, 1,0);
//	TEXTUREARRAY(4, 0,0.25f); TEXTUREARRAY(5, 0.5f,0.25f); TEXTUREARRAY(6, 0.5f,0.25f); TEXTUREARRAY(7, 1,0.25f);
//	TEXTUREARRAY(8, 0,0.75f); TEXTUREARRAY(9, 0.5f,0.75f); TEXTUREARRAY(10, 0.5f,0.75f); TEXTUREARRAY(11, 1,0.75f);
//	TEXTUREARRAY(12, 0,1); TEXTUREARRAY(13, 0.5f,1); TEXTUREARRAY(14, 0.5f,1); TEXTUREARRAY(15, 1,1);
//
//#undef TEXTUREARRAY

	glGenBuffers(1, &m_indexBuffer);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER,
		index_count * sizeof(GLushort),
		index_array, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


	glGenBuffers(1, &m_vertexBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
	glBufferData(GL_ARRAY_BUFFER,
		vertex_count * 3 * sizeof(GLfixed),
		vertex_array, GL_STATIC_DRAW);

	glGenBuffers(1, &m_texCoordBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer);
	glBufferData(GL_ARRAY_BUFFER,
		vertex_count * 2 * sizeof(GLfixed),
		texture_array, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

}
